/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pvtracejava;

import java.io.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Stack;
import org.apache.commons.io.FilenameUtils;

/**
 *
 * @author Administrator
 */
public class SymbolMonitor {

    private String execute = "";
    private Stack<String> funStack = new Stack<String>();
    private ArrayList<funInfo> funInfoList = new ArrayList<funInfo>();
    private Hashtable<String, Integer> funskip = new Hashtable<String, Integer>();
    private String currentName = "";

    public SymbolMonitor(String exebin) {
        execute = exebin;
    }

    void SymBol(String line) throws IOException, InterruptedException {
        String tmpValue = line.substring(1).toLowerCase().trim();
        if (line.isEmpty() ||isSkipValue(tmpValue)) {
            return;
        }
        currentName = funName(tmpValue);
        if (currentName.trim().isEmpty() || AddressisZero(tmpValue) || isUnknownAddressName(currentName)) {
            funskip.put(tmpValue, Integer.SIZE);
            return;
        }
        if (currentName.charAt(0) =='~') {
            currentName = currentName.replaceAll("~", "UNLOADFUN_");
        }

        if (line.toLowerCase().charAt(0) == 'e') {

            addSymbol(tmpValue);
            addCallTrace(tmpValue);
            funStack.push(tmpValue);
        } else if (line.toLowerCase().charAt(0) == 'x') {

            funStack.pop();
        }
    }

    void emitSymbols() throws IOException {
        File fp = new File("graph.dot");
        if (!fp.exists()) {
            fp.createNewFile();
        } else {
            fp.delete();
            fp.createNewFile();

        }
        BufferedWriter f = new BufferedWriter(new FileWriter(fp));
        f.write("digraph " + FilenameUtils.getBaseName(execute) + " {");
        f.newLine();
        f.newLine();
        for (funInfo Item : funInfoList) {
            for (funInfo Itme2 : funInfoList) {
                if (Item.In(Itme2)) {
                    Item.PlusCall();
                }
            }
            if (Item.GetCalled() > 0) {
                f.write(Item.getfunName() + " [shape=rectangle]");
            } else {
                f.write(Item.getfunName() + " [shape=ellipse] ");
            }
            f.newLine();
        }
        for (funInfo from : funInfoList) {
            for (funInfo to : funInfoList) {
                if (from.In(to)) {

                    f.write(String.format(" %s -> %s [label=\"%d calls\" fontsize=\"10\"] ", from.getfunName(), to.getfunName(), from.InNum(to)));
                    f.newLine();
                }
            }
        }
        f.newLine();
        f.write("}");
        f.newLine();
        f.flush();
        f.close();


    }

    private void addSymbol(String line) throws IOException, InterruptedException {

        for (funInfo item : funInfoList) {
            if (item.getAddress().compareToIgnoreCase(line) == 0) {
                return;
            }
        }
        funInfo finfo = new funInfo(line, currentName);

        funInfoList.add(finfo);

    }

    private boolean AddressisZero(String line) {


        if (Until.hex2decimal(line) == 0 ) {
            return true;
        }
        return false;
    }

    private String funName(String line) throws IOException, InterruptedException {
        Process CmdProcess = Runtime.getRuntime().exec("addr2line -e " + execute + " -f -s " + line);
        CmdProcess.waitFor();

        BufferedReader brCleanUp = new BufferedReader(new InputStreamReader(CmdProcess.getInputStream()));
        return brCleanUp.readLine().trim();
    }

    private void addCallTrace(String tmpValue) throws IOException, InterruptedException {
        if (!funStack.empty()) {
            for (funInfo Item : funInfoList) {
                if (Item.getAddress().compareToIgnoreCase(funStack.lastElement()) == 0) {
                    Item.Call(tmpValue);
                   break;
                }

            }
        }
    }

    private boolean isUnknownAddressName(String funName) {
        if (funName.compareToIgnoreCase("??") == 0) {
            return true;
        }
        return false;
    }

    private boolean isSkipValue(String tmpValue) {
        if (funskip.containsKey(tmpValue)) {
            return true;
        }
        return false;
    }
}
